﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Windows.Media;
using System.IO;
using System.Windows.Media.Imaging;

namespace Skyper.Model {
	public sealed class ContactModel : BaseModel,IEquatable<ContactModel>,IComparable<ContactModel> {
		#region Nested
		public sealed class UsernameEqualityComparer : IEqualityComparer<ContactModel> {
			public bool Equals(ContactModel x, ContactModel y) {
				if (Object.ReferenceEquals(x, y)) {
					return true;
				}
				if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) {
					return false;
				}
				if (String.IsNullOrEmpty(x.username) || String.IsNullOrEmpty(y.username)) {
					return false;
				}
				return (x.username == y.username);
			}
			public int GetHashCode(ContactModel obj) {
				if (Object.ReferenceEquals(obj, null)) {
					return 0;
				}
				if (String.IsNullOrEmpty(obj.username)) {
					return 0;
				}
				return obj.username.GetHashCode();
			}
		}
		public sealed class StandardComparer : IComparer<ContactModel> {

			public int Compare(ContactModel x, ContactModel y) {
				if (!Object.ReferenceEquals(x, null) && Object.ReferenceEquals(y, null)) {
					return 1;
				}
				if (Object.ReferenceEquals(x, null) && !Object.ReferenceEquals(y, null)) {
					return -1;
				}
				if (Object.ReferenceEquals(x, y)) {
					return 0;
				}
				return String.Compare(x.username, y.username);
			}
		}
		public sealed class FullnameComparer : IComparer<ContactModel> {

			public int Compare(ContactModel x, ContactModel y) {
				if (!Object.ReferenceEquals(x, null) && Object.ReferenceEquals(y, null)) {
					return 1;
				}
				if (Object.ReferenceEquals(x, null) && !Object.ReferenceEquals(y, null)) {
					return -1;
				}
				if (Object.ReferenceEquals(x, y)) {
					return 0;
				}
				return String.Compare(x.FriendlyName, y.FriendlyName);
			}
		}
		#endregion
		#region Fields
		private static readonly ImageSource DefaultAvatar = new System.Windows.Media.Imaging.BitmapImage(new Uri("/Images/General/Contact_48x48.png",UriKind.Relative));
		private String username;
		private String displayName;
		private String fullname;
		private String country;
		private String city;
		private String homepage;
		private String mood;
		private String phoneHome;
		private String phoneMobile;
		private String phoneWork;
		private Stream avatar;
		private UserStatus status;
		public static readonly IEqualityComparer<ContactModel> DefaultComparer = new UsernameEqualityComparer();
		public static readonly ContactModel DesignerPreview = new ContactModel() {
			City = "London",
			Country = "United Kingdom",
			DisplayName = "John Smith",
			Fullname = "Johnaton Smithtor",
			Homepage = "http://www.google.com/",
			Mood = "Hello, i'm john.",
			PhoneHome = "+1-(777)-77777",
			PhoneMobile = "+1-(777)-77777",
			PhoneWork = "+1-(777)-77777",
			Status = UserStatus.Online,
			Username = "johnsmith"
		};
		#endregion
		#region Private
		private void OnNameChanges() {
			this.OnPropertyChanged("Username");
			this.OnPropertyChanged("FriendlyName");
			this.OnPropertyChanged("DisplayName");
			this.OnPropertyChanged("Fullname");
		}
		#endregion
		#region Public
		public int CompareTo(ContactModel other) {
			return String.Compare(this.username, other.username);
		}
		/// <summary>
		/// Returns a <see cref="System.String"/> that represents this instance.
		/// </summary>
		/// <returns>
		/// A <see cref="System.String"/> that represents this instance.
		/// </returns>
		public sealed override string ToString() {
			return String.Format("[{0}] {1}", this.status.ToString(), this.FriendlyName);
		}
		/// <summary>
		/// Returns a hash code for this instance.
		/// </summary>
		/// <returns>
		/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
		/// </returns>
		public sealed override int GetHashCode() {
			if (String.IsNullOrEmpty(this.username)) {
				return 0;
			}
			return this.username.GetHashCode();
		}
		/// <summary>
		/// Compares with other contact.
		/// </summary>
		/// <param name="other">The other.</param>
		/// <returns></returns>
		public bool Equals(ContactModel other) {
			return DefaultComparer.Equals(other);
		}
		/// <summary>
		/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
		/// </summary>
		/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
		/// <returns>
		///   <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
		/// </returns>
		public sealed override bool Equals(Object obj) {
			if (obj == null) {
				return false;
			}
			if (obj is ContactModel) {
				return this.Equals((ContactModel)obj);
			}
			return false;
		}
		/// <summary>
		/// Updates this contact, by other contacts data.
		/// </summary>
		/// <param name="other">The other.</param>
		public void Update(ContactModel other) {
			this.Username = other.username;
			this.City = other.city;
			this.Country = other.country;
			this.DisplayName = other.displayName;
			this.Fullname = other.fullname;
			this.Homepage = other.homepage;
			this.Mood = other.mood;
			this.PhoneHome = other.phoneHome;
			this.PhoneMobile = other.phoneMobile;
			this.PhoneWork = other.phoneWork;
			this.Status = other.status;
		}
		/// <summary>
		/// Suspends this model to XElement.
		/// </summary>
		/// <returns>
		/// XElement or Null if suspending is not possible.
		/// </returns>
		public sealed override XElement Suspend() {
			XElement node = new XElement("Contact");

			node.SetAttributeValue("Username", this.username);
			node.SetAttributeValue("Status", (int)this.status);

			if (!String.IsNullOrEmpty(this.displayName)) {
				node.SetAttributeValue("DisplayName", this.displayName);
			}
			if (!String.IsNullOrEmpty(this.fullname)) {
				node.SetAttributeValue("Fullname", this.fullname);
			}
			if (!String.IsNullOrEmpty(this.country)) {
				node.SetAttributeValue("Country", this.country);
			}
			if (!String.IsNullOrEmpty(this.city)) {
				node.SetAttributeValue("City", this.city);
			}
			if (!String.IsNullOrEmpty(this.homepage)) {
				node.SetAttributeValue("Homepage", this.homepage);
			}
			if (!String.IsNullOrEmpty(this.mood)) {
				node.SetAttributeValue("Mood", this.mood);
			}

			if (!String.IsNullOrEmpty(this.phoneHome)) {
				node.SetAttributeValue("PhoneHome", this.phoneHome);
			}
			if (!String.IsNullOrEmpty(this.phoneWork)) {
				node.SetAttributeValue("PhoneWork", this.phoneWork);
			}
			if (!String.IsNullOrEmpty(this.phoneMobile)) {
				node.SetAttributeValue("PhoneMobile", this.phoneMobile);
			}

			return node;
		}
		/// <summary>
		/// Resumes this element, with specified node.
		/// </summary>
		/// <param name="node">The node.</param>
		/// <returns>
		/// True, if resuming proccess was success.
		/// </returns>
		public sealed override bool Resume(XElement node) {
			if (node == null || node.Name != "Contact") {
				return false;
			}

			this.username = node.GetAttributeValue("Username");
			this.displayName = node.GetAttributeValue("DisplayName");
			this.fullname = node.GetAttributeValue("Fullname");
			this.country = node.GetAttributeValue("Country");
			this.city = node.GetAttributeValue("City");
			this.homepage = node.GetAttributeValue("Homepage");
			this.mood = node.GetAttributeValue("Mood");
			this.status = (UserStatus)int.Parse(node.GetAttributeValue("Status"));
			this.phoneHome = node.GetAttributeValue("PhoneHome");
			this.phoneWork = node.GetAttributeValue("PhoneWork");
			this.phoneMobile = node.GetAttributeValue("PhoneMobile");

			return true;
		}
		public void SetAvatar(Byte[] data) {
			this.SetAvatar(new MemoryStream(data));
		}
		public void SetAvatar(Stream stream) {
			this.Avatar = stream;
		}
		#endregion
		#region Property
		/// <summary>
		/// Gets or sets the username.
		/// </summary>
		/// <value>The username.</value>
		public String Username {
			get {
				return this.username;
			}
			set {
				if (this.username != value) {
					this.username = value;
					this.OnNameChanges();
				}
			}
		}
		/// <summary>
		/// Gets or sets the display name.
		/// </summary>
		/// <value>The display name.</value>
		public String DisplayName {
			get {
				return this.displayName;
			}
			set {
				if (this.displayName != value) {
					this.displayName = value;
					this.OnNameChanges();
				}
			}
		}
		/// <summary>
		/// Gets or sets the fullname.
		/// </summary>
		/// <value>The fullname.</value>
		public String Fullname {
			get {
				return this.fullname;
			}
			set {
				if (this.fullname != value) {
					this.fullname = value;
					this.OnNameChanges();
				}
			}
		}
		/// <summary>
		/// Gets the name of the friendly.
		/// </summary>
		/// <value>The name of the friendly.</value>
		public String FriendlyName {
			get {
				if (!String.IsNullOrEmpty(this.displayName)) {
					return this.displayName;
				}
				if (!String.IsNullOrEmpty(this.fullname)) {
					return this.fullname;
				}
				return this.username;
			}
		}
		/// <summary>
		/// Gets or sets the country.
		/// </summary>
		/// <value>The country.</value>
		public String Country {
			get {
				return this.country;
			}
			set {
				if (this.country != value) {
					this.country = value;
					this.OnPropertyChanged("Country");
				}
			}
		}
		/// <summary>
		/// Gets or sets the city.
		/// </summary>
		/// <value>The city.</value>
		public String City {
			get {
				return this.city;
			}
			set {
				if (this.city != value) {
					this.city = value;
					this.OnPropertyChanged("City");
				}
			}
		}
		/// <summary>
		/// Gets or sets the homepage.
		/// </summary>
		/// <value>The homepage.</value>
		public String Homepage {
			get {
				return this.homepage;
			}
			set {
				if (this.homepage != value) {
					this.homepage = value;
					this.OnPropertyChanged("Homepage");
				}
			}
		}
		/// <summary>
		/// Gets or sets the mood.
		/// </summary>
		/// <value>The mood.</value>
		public String Mood {
			get {
				return this.mood;
			}
			set {
				if (this.mood != value) {
					this.mood = value;
					this.OnPropertyChanged("Mood");
				}
			}
		}

		/// <summary>
		/// Gets or sets the status.
		/// </summary>
		/// <value>The status.</value>
		public UserStatus Status {
			get {
				return this.status;
			}
			set {
				if (this.status != value) {
					this.status = value;
					this.OnPropertyChanged("Status");
					this.OnPropertyChanged("Available");
				}
			}
		}
		/// <summary>
		/// Gets a value indicating whether this <see cref="ContactModel"/> is available to chat with.
		/// </summary>
		/// <value><c>true</c> if available; otherwise, <c>false</c>.</value>
		public Boolean Available {
			get {
				return (this.status != UserStatus.Unknown && this.status != UserStatus.SkypeOut && this.status != UserStatus.Offline);
			}
		}

		/// <summary>
		/// Gets or sets the phone home.
		/// </summary>
		/// <value>The phone home.</value>
		public String PhoneHome {
			get {
				return this.phoneHome;
			}
			set {
				if (this.phoneHome != value) {
					this.phoneHome = value;
					this.OnPropertyChanged("PhoneHome");
				}
			}
		}
		/// <summary>
		/// Gets or sets the phone mobile.
		/// </summary>
		/// <value>The phone mobile.</value>
		public String PhoneMobile {
			get {
				return this.phoneMobile;
			}
			set {
				if (this.phoneMobile != value) {
					this.phoneMobile = value;
					this.OnPropertyChanged("PhoneMobile");
				}
			}
		}
		/// <summary>
		/// Gets or sets the phone work.
		/// </summary>
		/// <value>The phone work.</value>
		public String PhoneWork {
			get {
				return this.phoneWork;
			}
			set {
				if (this.phoneWork != value) {
					this.phoneWork = value;
					this.OnPropertyChanged("PhoneWork");
				}
			}
		}
		public Stream Avatar {
			get {
				return avatar;
			}
			set {

				avatar = value;
				this.OnPropertyChanged("Avatar");
			}
		}
		#endregion
	}
}
